import React from 'react';
import Canvas from 'ochart/core/Canvas';
import AxisLayer from 'ochart/layer/AxisLayer';
import KBarLayer from 'ochart/layer/KBarLayer';
import Event from 'ochart/event/Event';
import AccelerateAction from 'ochart/action/AccelerateAction';
import IndicatorLayer from "ochart/layer/IndicatorLayer";
import DrawLayer from 'ochart/layer/DrawLayer';
import OChart from 'ochart/core/OChart';
import PriceLine from 'ochart/layer/PriceLayer';
import CurveLayer from 'ochart/layer/CurveLayer';
import OverlyingLayer from 'ochart/layer/OverlyingLayer';
import HorizonLayer from 'ochart/layer/HorizonLayer';

function rateToBar(isNew, data, price) {
    if (isNew) {
        data[data.length - 1].close = parseFloat(price);
        if (data[data.length - 1].high < parseFloat(price)) {
            data[data.length - 1].high = parseFloat(price);
        }
        if (data[data.length - 1].low > parseFloat(price)) {
            data[data.length - 1].low = parseFloat(price);
        }
        data[data.length - 1].datetime = new Date().getTime();
    } else {
        data.push({
            close: parseFloat(price),
            high: parseFloat(price),
            low: parseFloat(price),
            open: parseFloat(price),
            datetime: new Date().getTime()
        });
    }
}

export default class H5Chart extends React.Component {
    constructor(props) {
        super(props);
        this.chartRef = React.createRef();
        this.overlyingData = [];
        this.horizonData = [];
    }

    /**
     * 动态更新叠加品种
     * @param overlying
     * @param price
     * @param ktype
     */
    updateOverlying(overlying, price, ktype) {
        const { data } = overlying;
        if (data.length === 0) {
            data.push({
                close: parseFloat(price),
                high: parseFloat(price),
                low: parseFloat(price),
                open: parseFloat(price),
                datetime: new Date().getTime()
            });
        } else if (data.length === 1) {

        } else {
            const lastTime = parseInt(data[data.length - 2].datetime);
            const currentTime = new Date().getTime();
            if (ktype === 0) {
                // 1分钟线
                rateToBar(lastTime + 60 * 1000 > currentTime, data, price);
            } else if (ktype === 1) {
                // 5分钟线
                rateToBar(lastTime + 5 * 60 * 1000 > currentTime, data, price);
            } else if (ktype === 2) {
                // 15分钟
                rateToBar(lastTime + 15 * 60 * 1000 > currentTime, data, price);
            } else if (ktype === 3) {
                // 30分钟线
                rateToBar(lastTime + 30 * 60 * 1000 > currentTime, data, price);
            } else if (ktype === 4) {
                // 小时线
                rateToBar(lastTime + 60 * 60 * 1000 > currentTime, data, price);
            } else if (ktype === 5) {
                // 4小时线
                rateToBar(lastTime + 4 * 60 * 60 * 1000 > currentTime, data, price);
            } else if (ktype === 6) {
                // 日线
                rateToBar(lastTime + 24 * 60 * 60 * 1000 > currentTime, data, price);
            } else if (ktype === 7) {
                rateToBar(lastTime + 7 * 24 * 60 * 60 * 1000 > currentTime, data, price);
            } else if (ktype === 8) {
                rateToBar(lastTime + 30 * 24 * 60 * 60 * 1000 > currentTime, data, price);
            }
        }
        overlying.data = data;
        this.overlying.make();
        this.canvas.paint();
    }

    /**
     * 动态更新数据
     * **/
    updateRate(price, ktype) {
        const { data } = this.barLayer;
        if (data.length === 0) {
            data.push({
                close: parseFloat(price),
                high: parseFloat(price),
                low: parseFloat(price),
                open: parseFloat(price),
                datetime: new Date().getTime()
            });
        } else if (data.length === 1) {

        } else {
            const lastTime = parseInt(data[data.length - 2].datetime);
            const currentTime = new Date().getTime();
            if (ktype === 0) {
                // 1分钟线
                rateToBar(lastTime + 60 * 1000 > currentTime, data, price);
            } else if (ktype === 1) {
                // 5分钟线
                rateToBar(lastTime + 5 * 60 * 1000 > currentTime, data, price);
            } else if (ktype === 2) {
                // 15分钟
                rateToBar(lastTime + 15 * 60 * 1000 > currentTime, data, price);
            } else if (ktype === 3) {
                // 30分钟线
                rateToBar(lastTime + 30 * 60 * 1000 > currentTime, data, price);
            } else if (ktype === 4) {
                // 小时线
                rateToBar(lastTime + 60 * 60 * 1000 > currentTime, data, price);
            } else if (ktype === 5) {
                // 4小时线
                rateToBar(lastTime + 4 * 60 * 60 * 1000 > currentTime, data, price);
            } else if (ktype === 6) {
                // 日线
                rateToBar(lastTime + 24 * 60 * 60 * 1000 > currentTime, data, price);
            } else if (ktype === 7) {
                rateToBar(lastTime + 7 * 24 * 60 * 60 * 1000 > currentTime, data, price);
            } else if (ktype === 8) {
                rateToBar(lastTime + 30 * 24 * 60 * 60 * 1000 > currentTime, data, price);
            }
        }
        this.barLayer.data = data;
        this.drawLayer.xAxis = data.map(item => item.datetime);
        this.barLayer.make();
        this.canvas.paint();
    }

    componentWillReceiveProps(nextProps, nextContext) {
        const { data } = nextProps;
        if (data !== this.props.data) {
            this.barLayer.data = data;
            this.drawLayer.xAxis = data.map(item => item.datetime);
            this.barLayer.make();
            this.canvas.paint();
        }
    }

    onChartDrag = (e) => {
        if (this.barLayer.locked) {
            return;
        }
        if (this.barLayer.position.x + e.distanceX > this.barLayer.data.length * this.barLayer.barWidth) {
            this.barLayer.setPosition(this.barLayer.data.length * this.barLayer.barWidth, this.barLayer.position.y);
        } else if(this.barLayer.position.x < -200){
            this.barLayer.setPosition(-200, this.barLayer.position.y)
        }else {
            this.barLayer.setPosition(this.barLayer.position.x + e.distanceX, this.barLayer.position.y);
        }
        this.barLayer.make();
        this.canvas.paint();
    }

    onChartScale = (e) => {
        if(this.barLayer.locked) {
            return;
        }
        if (e.nativeEvent.wheelDeltaY < 0) {
            if (this.barLayer.barNum < this.barLayer.data.length) {
                this.barLayer.barNum = Math.round(this.barLayer.barNum * 1.1);
                this.barLayer.make();
                this.canvas.paint();
            }
        } else {
            if (this.barLayer.barNum > 20) {
                this.barLayer.barNum = Math.round(this.barLayer.barNum * 0.9);
                this.barLayer.make();
                this.canvas.paint();
            }
        }
    }

    onChartDragEnd = (e) => {
        if (this.barLayer.locked) {
            return;
        }
        const accelerate = e.speedX > 0 ? 3000 : -3000; // 加速度200像素每秒
        const duration = Math.abs(e.speedX / accelerate);
        this.accelerateAction = new AccelerateAction(duration, {
            speedX: e.speedX,
            accelerateX: accelerate,
            beforeUpdate: (node, frame) => {
                node.make();
            }
        });
        this.barLayer.runAction(this.accelerateAction, (node, action) => {
            if (node.position.x > node.data.length * node.barWidth) {
                node.setPosition(node.data.length * node.barWidth, node.position.y);
                node.stopAction(action);
            } else if(node.position.x < -200) {
                node.setPosition(-200, node.position.y);
                node.stopAction(action);
            }
        })
    }

    componentDidMount() {
        const { data, lines = [], name = '' } = this.props;
        this.canvas = new Canvas({
            ele: this.chartRef.current,
            canAction: false
        });
        this.axisLayer = new AxisLayer(this.canvas, {
            yAxisMax: 100,
            yAxisMin: 0,
            xAxisLabels: [],
            xAxisPosition: AxisLayer.AxisPosition.BLOCK,
            xAxisGraduations: 5,
            yAxisGraduations: 3,
            yAxisRender: (data) => {
                return {
                    text: parseFloat(data).toFixed(2),
                    size: 20,
                }
            }
        });
        this.barLayer = new KBarLayer(this.canvas, {
            width: this.canvas.width,
            height: this.canvas.height - 160,
            data: data,
            barNum: 100,
        });
        this.barLayer.setPosition(-120, 80);
        this.indicatorLayer = new IndicatorLayer(this.canvas, {
            width: this.canvas.width,
            height: this.canvas.height - 160,
            data: data,
            name: 'boll'
        });
        this.indicatorLayer.setPosition(0, 80);
        this.barLayer.indicatorLayer = this.indicatorLayer;
        this.canvas.addChild(this.axisLayer, this.barLayer, this.indicatorLayer);
        this.barLayer.addEventListener(Event.EVENT_TAP, (e) => {
            if (this.accelerateAction !== null && this.accelerateAction !== undefined) {
                this.barLayer.stopAction(this.accelerateAction);
            }
        });
        this.barLayer.addEventListener(Event.EVENT_DRAG, (e) => {
            this.onChartDrag(e);
        });
        this.barLayer.addEventListener(Event.EVENT_DRAG_END, (e) => {
            this.onChartDragEnd(e);
        });
        OChart.on(`${name}:onQuoteDrag`, (e) => {
            this.onChartDrag(e);
        });
        OChart.on(`${name}:onQuoteDragEnd`, (e) => {
            this.onChartDragEnd(e);
        });
        this.barLayer.addEventListener(Event.EVENT_MOUSE_WHEEL, (e) => {
            this.onChartScale(e);
        });
        OChart.on(`${name}:onQuoteScale`, (e) => {
           this.onChartScale(e);
        });
        this.drawLayer = new DrawLayer(this.canvas, {
            width: this.canvas.width,
            height: this.canvas.height - 80,
            lines,
            onLineMove: () => {
                this.barLayer.locked = true;
            },
            onLineMoved: () => {
                this.barLayer.locked = false;
            }
        });
        this.drawLayer.setPosition(0, 80);
        this.canvas.addChild(this.drawLayer);
        this.priceLine = new PriceLine(this.canvas, {
            width: this.canvas.width,
            height: this.canvas.height - 160,
        });
        this.priceLine.visible = false;
        this.canvas.addChild(this.priceLine);
        this.curveLayer = new CurveLayer(this.canvas, {
            width: this.canvas.width,
            height: this.canvas.height
        });
        this.curveLayer.addEventListener(this.canvas.version === 'PC' ? Event.EVENT_LONG_CLICK : Event.EVENT_LONG_TAP, (e) => {
            const point = e.eventPoint;
            this.barLayer.locked = true;
            this.curveLayer.locked = true;
            this.curveLayer.onRun(point, (shift, price) => {
                let bar = {
                    open: 0,
                    high: 0,
                    low: 0,
                    close: 0,
                    datetime: 0
                };
                if (this.barLayer.data[this.barLayer.barStart + shift] !== undefined) {
                    bar = this.barLayer.data[this.barLayer.barStart + shift];
                }
                this.onCurveCallback && this.onCurveCallback({
                    shift,
                    bar,
                    point,
                    price
                });
            });
            this.canvas.paint();
        });
        this.curveLayer.addEventListener(this.canvas.version === 'PC' ? Event.EVENT_MOUSE_MOVE : Event.EVENT_TOUCH_MOVE, (e) => {
            if (this.curveLayer.locked) {
                const point = e.eventPoint;
                this.curveLayer.onRun(point, (shift, price) => {
                    let bar = {
                        open: 0,
                        high: 0,
                        low: 0,
                        close: 0,
                        datetime: 0
                    };
                    if (this.barLayer.data[this.barLayer.barStart + shift] !== undefined) {
                        bar = this.barLayer.data[this.barLayer.barStart + shift];
                    }
                    this.onCurveCallback && this.onCurveCallback({
                        shift,
                        bar,
                        point,
                        price
                    });
                });
                this.canvas.paint();
            }
        });
        this.curveLayer.addEventListener(this.canvas.version === 'PC' ? Event.EVENT_MOUSE_UP : Event.EVENT_TOUCH_END, (e) => {
            this.curveLayer.locked = false;
            const ret = this.onCurveEnd && this.onCurveEnd();
            if (ret === undefined || ret === false) {
                this.curveLayer.onStop();
            }
        });
        this.curveLayer.visible = false;
        this.canvas.addChild(this.curveLayer);
        this.overlying = new OverlyingLayer(this.canvas, {
            width: this.canvas.width,
            height: this.canvas.height - 160,
        });
        this.overlying.setPosition(0, 80);
        this.canvas.addChild(this.overlying);
        this.horizonLayer = new HorizonLayer(this.canvas, {
            width: this.canvas.width,
            height: this.canvas.height,
            data: this.horizonData,
        });
        this.canvas.addChild(this.horizonLayer);
        this.barLayer.setOnMaked((layer, option) => {
            const { max, min, start, end, barWidth, yDelta } = option;
            let yAxisMax = max + 80 / yDelta;
            let yAxisMin = min - 80 / yDelta;
            this.axisLayer.yAxisMax =  yAxisMax;
            this.axisLayer.yAxisMin = yAxisMin;
            this.drawLayer.start = start;
            this.drawLayer.end = end;
            this.drawLayer.min = yAxisMin;
            this.drawLayer.max = yAxisMin;
            this.drawLayer.xStep = barWidth;
            this.drawLayer.yStep = yDelta;
            this.priceLine.price = this.barLayer.data[this.barLayer.data.length - 1].close;
            this.priceLine.yStep = yDelta;
            this.priceLine.yBase = yAxisMin;
            this.curveLayer.xStep = barWidth;
            this.curveLayer.yStep = yDelta;
            this.curveLayer.min = yAxisMin;
            this.curveLayer.max = yAxisMax;
            this.overlying.xStep = barWidth;
            this.overlying.start = this.barLayer.data.length - start;
            this.overlying.end = this.barLayer.data.length - end;
            this.overlying.data = this.overlyingData;
            this.horizonLayer.yStep = yDelta;
            this.horizonLayer.base = yAxisMin;
            this.horizonLayer.make();
            this.overlying.make();
            this.priceLine.make();
            this.drawLayer.make();
            this.axisLayer.make();
            OChart.emit(`${name}:onChartMaked`, {
                start,
                end,
                xStep: barWidth,
                yStep: yDelta,
                max: yAxisMax,
                min: yAxisMin
            });
        });
        OChart.on(`${name}:drawLine`, (options) => {
           // 线条绘制监听
            const x1 = Math.round(
              (this.barLayer.barEnd - this.barLayer.barStart) / 3 + this.barLayer.barStart
            );
            const x2 = Math.round(
              (this.barLayer.barEnd - this.barLayer.barStart) / 3 * 2 + this.barLayer.barStart
            );
            const price1 = (this.barLayer.max - this.barLayer.min) / 3 + this.barLayer.min;
            const price2 = (this.barLayer.max - this.barLayer.min) / 3 * 2 + this.barLayer.min;
            const point1 = {
                date: this.barLayer.data[x1].datetime,
                price: price1
            };
            const point2 = {
                date: this.barLayer.data[x2].datetime,
                price: price2
            };
            const target = {
              point1,
              point2
            };
            if (options.type === 0) {
                target.type = 0;
            } else if(options.type === 1) {
                target.type = 1;
            } else if(options.type === 2) {
                target.type = 2;
                target.rules = options.rules || [0, 23.6, 38.2, 50, 61.8, 80.9, 100];
            } else {
                target.type = 0;
            }
            target.color = options.color;
            target.isSelected = true;
            this.drawLayer.clearSelected();
            this.drawLayer.lines.push(target);
            this.drawLayer.make();
            this.canvas.paint();
            options.onCreated && options.onCreated(this.drawLayer.lines);
        });
        OChart.on(`${name}:onDrawLineMove`, ({callback}) => {
           this.drawLayer.onLineMove = (line) => {
               this.barLayer.locked = true;
               callback && callback(line);
           };
        });
        OChart.on(`${name}:onDrawLineMoved`, ({callback}) => {
            this.drawLayer.onLineMoved = (line) => {
                this.barLayer.locked = false;
                callback && callback(line);
            };
        });
        OChart.on(`${name}:deleteLine`, ({uuid}) => {
           const index = this.drawLayer.lines.findIndex(item => item.uuid === uuid);
           if (index >= 0) {
               this.drawLayer.lines.splice(index, 1);
               this.drawLayer.make();
               this.canvas.paint();
           }
        });
        OChart.on(`${name}:onDrawLineSelected`, ({callback}) => {
            console.log(callback)
           this.drawLayer.onSelected = callback;
        });
        OChart.on(`${name}:editLine`, (options) => {
            const index = this.drawLayer.lines.findIndex(item => item.uuid === options.uuid);
            if(index >= 0){
                this.drawLayer.lines[index].color = options.color || this.drawLayer.lines[index].color;
                this.drawLayer.make();
                this.canvas.paint();
            }
        });
        OChart.on(`${name}:updateRate`, (options) => {
           // 更新数据
            this.updateRate(options.price, options.ktype);
        });
        OChart.on(`${name}:setStyle`, (options) => {
           // 设置图表样式
            this.barLayer.lineType = options.lineType === undefined ? this.barLayer.lineType : options.lineType;
            this.barLayer.lineColor = options.lineColor || this.barLayer.lineColor;
            if (options.up) {
                this.barLayer.redColor = options.up.color || this.barLayer.redColor;
                this.barLayer.redHollow = options.up.solid === true;
            }
            if (options.down) {
                this.barLayer.greenColor = options.down.color || this.barLayer.greenColor;
                this.barLayer.greenHollow = options.down.solid === true;
            }
            this.barLayer.make();
            this.canvas.paint();
        });
        OChart.on(`${name}:setCurrentPriceStyle`, (options) => {
           this.priceLine.visible = options.visible === undefined ? this.priceLine.visible : options.visible;
           this.priceLine.color = options.color || this.priceLine.color;
           this.priceLine.backgroundColor = options.backgroundColor || this.priceLine.backgroundColor;
           this.priceLine.size = options.size || this.priceLine.size;
           this.priceLine.make();
           this.canvas.paint();
        });
        OChart.on(`${name}:setCurve`, (options) => {
            this.curveLayer.visible = options.visible === undefined ? this.priceLine.visible : options.visible;
            if (this.curveLayer.visible === false) {
                this.curveLayer.onStop();
            }
            this.curveLayer.color = options.color || this.curveLayer.color;
            this.curveLayer.backgroundColor = options.backgroundColor || this.curveLayer.backgroundColor;
            this.curveLayer.fontColor = options.fontColor || this.curveLayer.fontColor;
            this.curveLayer.size = options.size || this.curveLayer.size;
            if (options.onCurve) {
                this.onCurveCallback = options.onCurve;
            }
            if (options.onCurveEnd) {
                this.onCurveEnd = options.onCurveEnd;
            }
            if (options.onCurveLabel) {
                this.curveLayer.processData = options.onCurveLabel;
            }
        });
        OChart.on(`${name}:setMainQuota`, (options) => {
            this.indicatorLayer.name = options.quota;
            this.barLayer.make();
            this.canvas.paint();
        });
        OChart.on(`${name}:addOverlying`, (options) => {
           // 添加叠加品种
            console.log(options);
            let overlyings = this.overlyingData.filter(item => item.id === options.id);
            if (overlyings.length > 0) {
                let overlying = overlyings[0];
                overlying.data = options.data || overlying.data;
                if (options.up) {
                    if (overlying.up) {
                        overlying.up.color = options.up.color || overlying.up.color;
                        overlying.up.solid = options.up.solid || overlying.up.solid;
                    } else {
                        overlying.up = options.up;
                    }
                }
                if (options.down) {
                    if (overlying.down) {
                        overlying.down.color = options.down.color || overlying.down.color;
                        overlying.down.solid = options.down.solid || overlying.down.solid;
                    } else {
                        overlying.down = options.down;
                    }
                }
                overlying.lineType = options.lineType || overlying.lineType;
                overlying.lineColor = options.lineColor || overlying.lineColor;
            } else {
                this.overlyingData.push(options);
            }
            console.log(this.overlyingData);
            this.overlying.data = this.overlyingData;
            this.overlying.make();
            console.log(this.overlying);
            this.canvas.paint();
        });
        OChart.on(`${name}:removeOverlying`, ({overlyingId}) => {
           let index = this.overlyingData.findIndex(item => item.id = overlyingId);
           if (index >= 0) {
               this.overlyingData.splice(index, 1);
               this.overlying.data = this.overlyingData;
               this.overlying.make();
               this.canvas.paint();
           }
        });
        OChart.on(`${name}:updateOverlyingRate`, (options) => {
            let index = this.overlyingData.findIndex(item => item.id === options.id);
            if (index >= 0) {
                let overlying = this.overlyingData[index];
                this.updateOverlying(overlying, options.price, options.ktype);
            }
        });
        OChart.on(`${name}:addHorizon`, (options) => {
            this.horizonLayer.onMoved = options.onMove || this.horizonLayer.onMoved;
            this.horizonLayer.onSelected = options.onSelected || this.horizonLayer.onSelected;
            if (options.lines) {
                if (options.lines instanceof Array) {
                    options.lines.forEach(item => {
                       const index = this.horizonData.findIndex(vo => vo.id === item.id);
                       if (index === -1) {
                           this.horizonData.push(item);
                       } else {
                           this.horizonData[index].price = item.price || this.horizonData[index].price;
                           this.horizonData[index].color = item.color || this.horizonData[index].color;
                           this.horizonData[index].movable = item.movable || this.horizonData[index].movable;
                           this.horizonData[index].selectable = item.selectable || this.horizonData[index].selectable;
                           this.horizonData[index].legend = item.legend || this.horizonData[index].legend;
                       }
                    });
                } else if (options.lines instanceof Object) {
                    const index = this.horizonData.findIndex(vo => vo.id === options.lines.id);
                    if (index === -1) {
                        this.horizonData.push(options.lines);
                    } else {
                        this.horizonData[index].price = options.lines.price || this.horizonData[index].price;
                        this.horizonData[index].color = options.lines.color || this.horizonData[index].color;
                        this.horizonData[index].movable = options.lines.movable || this.horizonData[index].movable;
                        this.horizonData[index].selectable = options.lines.selectable || this.horizonData[index].selectable;
                        this.horizonData[index].legend = options.lines.legend || this.horizonData[index].legend;
                    }
                }
                this.horizonLayer.data = this.horizonData;
                this.horizonLayer.make();
                console.log(this.horizonLayer);
                this.canvas.paint();
            }
        });
        OChart.on(`${name}:setHorizonOnMove`, (options) => {
            this.horizonLayer.onMoved = options.onMove || this.horizonLayer.onMoved;
        });
        OChart.on(`${name}:setHorizonOnSelected`, (options) => {
            this.horizonLayer.onSelected = options.onSelected || this.horizonLayer.onSelected;
        });
        OChart.on(`${name}:removeHorizon`, (options) => {
            const { id } = options;
            const index = this.horizonData.findIndex(item => item.id === id);
            if (index !== -1) {
                this.horizonData.splice(index, 1);
                this.horizonLayer.data = this.horizonData;
                this.horizonLayer.make();
                this.canvas.paint();
            }
        });
        this.axisLayer.make();
        this.barLayer.make();
        this.canvas.paint();
    }

    render() {
        const { className = '' } = this.props;
        return (
            <div className={className} ref={this.chartRef} />
        )
    }

}
